﻿#region License

// PentaBiz - Sustainable Software Development Framework Copyright (C) 2013 Zoltán Csizmazia
// 
// This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
// 
// This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 

#endregion

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Security;
using PentaBiz.Diagnostics;

namespace PentaBiz.Cloud.Services.DynamicService.RemoteDomain
{
    [SecurityCritical]
    public abstract class RemoteProxy : MarshalByRefObject, IRemoteProxy
    {
        public const string ApplicationDbContextTypeName = "ApplicationDbContext";
        public const string ApplicationDbQueryContextTypeName = "ApplicationDbQueryContext";
        public const string ApplicationDataQueryServiceTypeName = "ApplicationDataQueryService";
        public const string ApplicationCommandServiceTypeName = "ApplicationCommandService";


        [DebuggerBrowsable(DebuggerBrowsableState.Never)] protected readonly List<Type> LoadedTypes = new List<Type>();


        private readonly AppDomain _remoteAppDomain;

        /// <summary>
        ///     Initializes a new instance of the <see cref="RemoteProxy" /> class.
        /// </summary>
        public RemoteProxy()
        {
            _remoteAppDomain = AppDomain.CurrentDomain;
            _remoteAppDomain.UnhandledException += RemoteAppDomainUnhandledException;
        }

        /// <summary>
        ///     Gets the exported types.
        /// </summary>
        /// <value>
        ///     The exported types.
        /// </value>
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        public Type[] ExportedTypes
        {
            get { return LoadedTypes.ToArray(); }
        }

        #region IRemoteProxy Members

        /// <summary>
        ///     Loads the assembly bytes.
        /// </summary>
        /// <param name="rawAssembly">The raw assembly.</param>
        /// <param name="rawSymbolStore">The raw symbol store.</param>
        public void LoadAssemblyBytes(byte[] rawAssembly, byte[] rawSymbolStore)
        {
            Trace.TraceInformation("Loading assembly by raw Bytes");
            if (rawSymbolStore != null)
                Trace.TraceInformation("Loading assembly with Symbol Store (Pdb)");

            //load the assembly
            Assembly assembly = rawSymbolStore == null
                                    ? RemoteAppDomain.Load(rawAssembly)
                                    : //dll only if no pdb has been set
                                    RemoteAppDomain.Load(rawAssembly, rawSymbolStore); //or dll and pdb if both present

            //add exported types to loaded types
            LoadedTypes.AddRange(assembly.GetExportedTypes());
        }


        /// <summary>
        ///     Loads the assembly file.
        /// </summary>
        /// <param name="assemblyPath">The assembly path.</param>
        public void LoadAssemblyFile(string assemblyPath)
        {
            Trace.TraceInformation("Loading assembly from file {0}", assemblyPath);

            string fullPath = Path.GetFullPath(assemblyPath);

            Assembly asm = Assembly.LoadFile(fullPath);
            LoadedTypes.AddRange(asm.GetExportedTypes());
        }

        /// <summary>
        ///     Gets the remote app domain.
        /// </summary>
        /// <value>
        ///     The remote app domain.
        /// </value>
        public AppDomain RemoteAppDomain
        {
            get { return _remoteAppDomain; }
        }

        #endregion

        private void RemoteAppDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            Trace.TraceError("Remote AppDomain {0} has an unhandled exception! IsTerminating {1}, {2}",
                             RemoteAppDomain.FriendlyName, e.IsTerminating, e.ExceptionObject);
        }
    }
}